<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="sync包补充">
<meta name="twitter:description" content="<p>条件变量sync.Cond的用法，方法介绍以及代码示例；并发安全字典sync.Map：原生字典在并发中的用法，并发安全字典的用法，两者的比较；并发安全字典进阶：用类型断言检查类型正确，用反射检查类型正确。</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/gotour-pkg-sync-advanced.html"/>
<meta property="og:title" content="来玩魔王的咚 | sync包补充" />
<meta property="og:description" content="<p>条件变量sync.Cond的用法，方法介绍以及代码示例；并发安全字典sync.Map：原生字典在并发中的用法，并发安全字典的用法，两者的比较；并发安全字典进阶：用类型断言检查类型正确，用反射检查类型正确。</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>条件变量sync.Cond的用法，方法介绍以及代码示例；并发安全字典sync.Map：原生字典在并发中的用法，并发安全字典的用法，两者的比较；并发安全字典进阶：用类型断言检查类型正确，用反射检查类型正确。</p>" />

    <meta name="tags" content="gotour" />
    <meta name="tags" content="sync" />
    <meta name="tags" content="反射" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li class="active"><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-11-30 11:00:00+08:00">
                <script>document.write(moment('2020-11-30 11:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-11-30 11:00:00+08:00">
                <script>document.write(moment('2020-12-01 21:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            sync包补充
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#tiao-jian-bian-liang-synccond">条件变量（sync.Cond）</a><ul>
<li><a href="#yong-fa-shi-li">用法示例</a></li>
<li><a href="#fang-fa-jie-shao">方法介绍</a></li>
<li><a href="#shi-yong-du-suo">使用读锁</a></li>
<li><a href="#jia-ru-tiao-jian-jian-cha">加入条件检查</a></li>
</ul>
</li>
<li><a href="#bing-fa-an-quan-zi-dian-syncmap">并发安全字典（sync.Map）</a><ul>
<li><a href="#yuan-sheng-zi-dian-de-shi-yong">原生字典的使用</a></li>
<li><a href="#bing-fa-an-quan-zi-dian">并发安全字典</a></li>
<li><a href="#bing-fa-an-quan-zi-dian-yu-yuan-sheng-zi-dian-de-bi-jiao">并发安全字典与原生字典的比较</a></li>
</ul>
</li>
<li><a href="#bing-fa-an-quan-zi-dian-jin-jie">并发安全字典进阶</a><ul>
<li><a href="#zi-dian-key-lei-xing-de-yao-qiu">字典key类型的要求</a></li>
<li><a href="#fang-an-yi-lei-xing-duan-yan">方案一：类型断言</a></li>
<li><a href="#fang-an-er-fan-she">方案二：反射</a></li>
<li><a href="#zong-jie">总结</a></li>
</ul>
</li>
</ul>
</div>
<p>补充几个sync包中的类型：</p>
<ul>
<li>sync.Cond 条件变量</li>
<li>sync.Map 并发安全字典</li>
</ul>
<p>另外还有<a href="/tag/sync.html">sync包的其他相关的内容</a> 。  </p>
<h3 id="tiao-jian-bian-liang-synccond"><a class="toclink" href="#tiao-jian-bian-liang-synccond">条件变量（sync.Cond）</a></h3>
<p>一项任务需要满足一定条件才可以继续执行，否则就一直等着。<br>
sync.Cond是用于实现条件变量（condition variable）的，具有阻塞和唤醒的功能。在满足条件时，可以唤醒继续执行。条件变量只是一种典型的场景（所以用处不止这一种情况？）。  </p>
<h4 id="yong-fa-shi-li"><a class="toclink" href="#yong-fa-shi-li">用法示例</a></h4>
<p>这个示例只为了说明使用方法，不是典型的使用场景。<br>
以田径短跑比赛为例，有几名运动员，还有一名裁判。每个人各一个goroutine。所有运动员都就位后，需要等裁判发令，之后运动员立刻开始跑。<em>为了不使示例太复杂，只有运动员会等待裁判发令立即响应。裁判直接简单的靠sleep确保所有运动员就位。</em><br>
示例代码：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">mutex</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span><span class="p">{}</span><span class="w">        </span><span class="c1">// 先创建一个锁</span><span class="w"></span>
<span class="w">    </span><span class="nx">cond</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">NewCond</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">mutex</span><span class="p">)</span><span class="w"> </span><span class="c1">// 生成一个cond，需要传入锁。这里传了指针</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">wg</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">6</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">num</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">num</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;号运动员就位&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">cond</span><span class="p">.</span><span class="nx">L</span><span class="p">.</span><span class="nx">Lock</span><span class="p">()</span><span class="w">         </span><span class="c1">// 获取锁</span><span class="w"></span>
<span class="w">            </span><span class="k">defer</span><span class="w"> </span><span class="nx">cond</span><span class="p">.</span><span class="nx">L</span><span class="p">.</span><span class="nx">Unlock</span><span class="p">()</span><span class="w"> </span><span class="c1">// 释放锁</span><span class="w"></span>
<span class="w">            </span><span class="nx">cond</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span><span class="w">           </span><span class="c1">// 阻塞，等待通知</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">num</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;号开始跑...&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}(</span><span class="nx">i</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 等2秒，所有运动员就位</span><span class="w"></span>
<span class="w">    </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;各就各位...&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">500</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;预备...&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">500</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;PONG !!!&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">cond</span><span class="p">.</span><span class="nx">Broadcast</span><span class="p">()</span><span class="w">  </span><span class="c1">// 发通知，广播所有cond</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>sync.Cond通过sync.NewCond(sync.Locker)初始化。<br>
sync.Locker其实是一个接口，包含Lock()和Unlock()方法。sync.Mutex和sync.RWMutex都有Lock和Unlock方法，所以都能用，具体看场景。但他们都是指针方法，只有指针类型才满足接口，所以参数传入的是指针类型。<br>
这个例子只有唤醒，没有条件检查。但是很好的说明了sync.Cond的用法。这里只有一个广播通知的效果，用关闭通道的方式，也能实现广播，所以场景还不够典型。  </p>
<h4 id="fang-fa-jie-shao"><a class="toclink" href="#fang-fa-jie-shao">方法介绍</a></h4>
<p>sync.Cond有三个方法：<br>
<strong>Wait</strong>，阻塞当前gorouting，直到被Boradcast方法或Signal方法唤醒。使用的时候需要加锁。<br>
<strong>Signal</strong>，唤醒一个等待的gorouting。<br>
<strong>Broadcast</strong>，唤醒所有等待的goroutine，就是广播。  </p>
<p>主要看下Wait的源码：</p>
<div class="highlight"><pre><span></span><code><span class="c1">//  源码中给的使用的示例，这里唤醒后，还要检查逻辑，就是for的条件condition</span><span class="w"></span>
<span class="c1">//  如果不符合条件，继续等待下次被唤醒</span><span class="w"></span>
<span class="c1">//  显然，这里condition的条件是要在别的goroutine里改变的</span><span class="w"></span>
<span class="c1">//    c.L.Lock()</span><span class="w"></span>
<span class="c1">//    for !condition() {</span><span class="w"></span>
<span class="c1">//        c.Wait()</span><span class="w"></span>
<span class="c1">//    }</span><span class="w"></span>
<span class="c1">//    ... make use of condition ...</span><span class="w"></span>
<span class="c1">//    c.L.Unlock()</span><span class="w"></span>
<span class="c1">//</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">Cond</span><span class="p">)</span><span class="w"> </span><span class="nx">Wait</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">c</span><span class="p">.</span><span class="nx">checker</span><span class="p">.</span><span class="nx">check</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">t</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">runtime_notifyListAdd</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">c</span><span class="p">.</span><span class="nx">notify</span><span class="p">)</span><span class="w">  </span><span class="c1">// 加入到通知队列里面</span><span class="w"></span>
<span class="w">    </span><span class="nx">c</span><span class="p">.</span><span class="nx">L</span><span class="p">.</span><span class="nx">Unlock</span><span class="p">()</span><span class="w">                           </span><span class="c1">// 解锁，所以Wait之前要上锁</span><span class="w"></span>
<span class="w">    </span><span class="nx">runtime_notifyListWait</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">c</span><span class="p">.</span><span class="nx">notify</span><span class="p">,</span><span class="w"> </span><span class="nx">t</span><span class="p">)</span><span class="w">   </span><span class="c1">// 等待，直到被唤醒。阻塞在这里</span><span class="w"></span>
<span class="w">    </span><span class="nx">c</span><span class="p">.</span><span class="nx">L</span><span class="p">.</span><span class="nx">Lock</span><span class="p">()</span><span class="w">                             </span><span class="c1">// 收到通知唤醒后，重新锁定</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里上锁和解锁使用的都是锁的Lock()和Unlock()方法。即使是读写锁，这两个方法对应的也是对写锁的操作。  </p>
<h4 id="shi-yong-du-suo"><a class="toclink" href="#shi-yong-du-suo">使用读锁</a></h4>
<p>使用读锁创建Cond的方法：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">lock</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">RWMutex</span><span class="w"></span>
<span class="nx">cond</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">NewCond</span><span class="p">(</span><span class="nx">lock</span><span class="p">.</span><span class="nx">RLocker</span><span class="p">())</span><span class="w"></span>
</code></pre></div>

<p>这里调用了读写锁的Rlocker()方法，把读锁传给了cond。传入的读锁的Lock()和Unlock()方法对应的就是读写锁的RLock()和RUnlock()方法。  </p>
<h4 id="jia-ru-tiao-jian-jian-cha"><a class="toclink" href="#jia-ru-tiao-jian-jian-cha">加入条件检查</a></h4>
<p>典型的用法是如源码中写的那样的，要用for循环来检查：</p>
<div class="highlight"><pre><span></span><code><span class="c1">//    c.L.Lock()</span><span class="w"></span>
<span class="c1">//    for !condition() {</span><span class="w"></span>
<span class="c1">//        c.Wait()</span><span class="w"></span>
<span class="c1">//    }</span><span class="w"></span>
<span class="c1">//    ... make use of condition ...</span><span class="w"></span>
<span class="c1">//    c.L.Unlock()</span><span class="w"></span>
</code></pre></div>

<p>如果用if，只能检查一次，而用for可以多次检查，知道状态正确为止。<br>
如果用通道来实现广播，通道只能关闭一次，也就只能广播一次。而这里可以多次广播。<br>
每次被唤醒后，再检查状态。如果状态正确就继续执行，否则继续等待。  </p>
<p>下面是一个生产者消费者模型的示例：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">type</span><span class="w"> </span><span class="nx">Mailbox</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">taskNum</span><span class="w"> </span><span class="kt">int</span><span class="w"></span>
<span class="w">        </span><span class="nx">msg</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">        </span><span class="nx">readFinished</span><span class="w"> </span><span class="kt">bool</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">mailbox</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Mailbox</span><span class="p">{</span><span class="nx">readFinished</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">}</span><span class="w">  </span><span class="c1">// 这个是共享变量，操作需要加锁</span><span class="w"></span>

<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">lock</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">RWMutex</span><span class="w"></span>
<span class="w">    </span><span class="nx">sendCond</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">NewCond</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">lock</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">recvCond</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">NewCond</span><span class="p">(</span><span class="nx">lock</span><span class="p">.</span><span class="nx">RLocker</span><span class="p">())</span><span class="w"></span>

<span class="w">    </span><span class="c1">// 开启10个消费者</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w">  </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">i</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">for</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="c1">// 加读锁</span><span class="w"></span>
<span class="w">                </span><span class="nx">lock</span><span class="p">.</span><span class="nx">RLock</span><span class="p">()</span><span class="w"></span>
<span class="w">                </span><span class="c1">// 未读过，并且取模是自己的序号才符合条件</span><span class="w"></span>
<span class="w">                </span><span class="c1">// 条件取反，就是不符合条件，这个就是需要等待唤醒的逻辑</span><span class="w"></span>
<span class="w">                </span><span class="k">for</span><span class="w"> </span><span class="p">!(!</span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">readFinished</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">taskNum</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                    </span><span class="nx">recvCond</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span><span class="w"></span>
<span class="w">                </span><span class="p">}</span><span class="w"></span>
<span class="w">                </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;消费者%d: %s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">msg</span><span class="p">)</span><span class="w"></span>
<span class="w">                </span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">readFinished</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">true</span><span class="w">  </span><span class="c1">// 这里修改值了，是否合适？</span><span class="w"></span>
<span class="w">                </span><span class="nx">lock</span><span class="p">.</span><span class="nx">RUnlock</span><span class="p">()</span><span class="w"></span>
<span class="w">                </span><span class="nx">sendCond</span><span class="p">.</span><span class="nx">Signal</span><span class="p">()</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="p">}(</span><span class="nx">i</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 主线程作为生产者</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="mi">100</span><span class="p">;</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="o">++</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="c1">// 第一次不需要等待，之后每次都停顿一下</span><span class="w"></span>
<span class="w">            </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">300</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">lock</span><span class="p">.</span><span class="nx">Lock</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="c1">// 读取完毕可以继续操作</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="p">!</span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">readFinished</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">sendCond</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">taskNum</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="w"></span>
<span class="w">        </span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">msg</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%03d Message&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">j</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">mailbox</span><span class="p">.</span><span class="nx">readFinished</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">false</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;生产者 : %03d\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">j</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">lock</span><span class="p">.</span><span class="nx">Unlock</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">recvCond</span><span class="p">.</span><span class="nx">Broadcast</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 还要等待最后一个任务被执行完</span><span class="w"></span>
<span class="w">    </span><span class="nx">lock</span><span class="p">.</span><span class="nx">Lock</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">sendCond</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span><span class="w">  </span><span class="c1">// 不要漏了上面和下面的加锁、解锁操作</span><span class="w"></span>
<span class="w">    </span><span class="nx">lock</span><span class="p">.</span><span class="nx">Unlock</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>自己看别人的例子，修改后完整的代码。运行没问题，能跑。不确定各方面是否合理，比如读锁中对mailbox.readFinished的修改。<br>
另外，其实可以把锁也放到结构体中。  </p>
<h3 id="bing-fa-an-quan-zi-dian-syncmap"><a class="toclink" href="#bing-fa-an-quan-zi-dian-syncmap">并发安全字典（sync.Map）</a></h3>
<p>Go语言自带的字典类型map，就是原生字典，并不是并发安全的。如果要在goroutine中修改字典的值，就需要并发安全的字典。  </p>
<h4 id="yuan-sheng-zi-dian-de-shi-yong"><a class="toclink" href="#yuan-sheng-zi-dian-de-shi-yong">原生字典的使用</a></h4>
<p>在使用原生字典的时候，应该在启动goroutine之前就完成字典的初始化和赋值。或者更高级的做法是，可以在goroutine中，在首次使用的时候通过sync.Once来并发安全的完成初始化和赋值的操作，达到一个延迟初始化的优化效果。之后在使用字典的时候，就只能获取其中的内容，不能再对其进行修改了。这个在讲sync.Once时有示例：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">loadIconsOnce</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">Once</span><span class="w"></span>
<span class="kd">var</span><span class="w"> </span><span class="nx">icons</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">image</span><span class="p">.</span><span class="nx">Image</span><span class="w"></span>

<span class="c1">// 这是个昂贵的初始化步骤</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">loadIcons</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">icons</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">image</span><span class="p">.</span><span class="nx">Image</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="s">&quot;spades.png&quot;</span><span class="p">:</span><span class="w">   </span><span class="nx">loadIcon</span><span class="p">(</span><span class="s">&quot;spades.png&quot;</span><span class="p">),</span><span class="w"></span>
<span class="w">        </span><span class="s">&quot;hearts.png&quot;</span><span class="p">:</span><span class="w">   </span><span class="nx">loadIcon</span><span class="p">(</span><span class="s">&quot;hearts.png&quot;</span><span class="p">),</span><span class="w"></span>
<span class="w">        </span><span class="s">&quot;diamonds.png&quot;</span><span class="p">:</span><span class="w"> </span><span class="nx">loadIcon</span><span class="p">(</span><span class="s">&quot;diamonds.png&quot;</span><span class="p">),</span><span class="w"></span>
<span class="w">        </span><span class="s">&quot;clubs.png&quot;</span><span class="p">:</span><span class="w">    </span><span class="nx">loadIcon</span><span class="p">(</span><span class="s">&quot;clubs.png&quot;</span><span class="p">),</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// 并发安全</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">Icon</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="nx">image</span><span class="p">.</span><span class="nx">Image</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">loadIconsOnce</span><span class="p">.</span><span class="nx">Do</span><span class="p">(</span><span class="nx">loadIcons</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">icons</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="bing-fa-an-quan-zi-dian"><a class="toclink" href="#bing-fa-an-quan-zi-dian">并发安全字典</a></h4>
<p>Go言语官方是在Go 1.9中才正式加入了并发安全的字典类型sync.Map。<br>
使用前无须初始化，直接声明即可。但是不能使用map的方式取值和设置值，需要调用sync.Map的方法，具体的方法如下：</p>
<ol>
<li><strong>Store</strong>：存储一对 key-value 值。</li>
<li><strong>Load</strong>：根据key获取对应的value值，并且可以判断key是否存在。</li>
<li><strong>LoadOrStore</strong>：如果key对应的value存在，则返回该value；如果不存在，存储相应的 value。</li>
<li><strong>Delete</strong>：删除一个 key-value 键值对。</li>
<li><strong>Range</strong>：循环迭代sync.Map。配合一个回调函数进行遍历操作，回调函数有返回值，如果为false也会停止遍历。</li>
</ol>
<p>下面就是使用并发安全字典的示例：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;sync&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">pairs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kd">struct</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">k</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">        </span><span class="nx">v</span><span class="w"> </span><span class="kt">int</span><span class="w"></span>
<span class="w">    </span><span class="p">}{</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;k1&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;k2&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;k3&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;k4&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">},</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>

<span class="w">    </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;创建map&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">cm</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">Map</span><span class="p">{}</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">pairs</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">cm</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">pairs</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">pairs</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">v</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>

<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;遍历输出map&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">cm</span><span class="p">.</span><span class="nx">Range</span><span class="w"> </span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%v: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="w"></span>
<span class="w">        </span><span class="p">})</span><span class="w"></span>

<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;Load 和 LoadOrStore 方法&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">key</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&quot;k3&quot;</span><span class="w"></span>
<span class="w">        </span><span class="nx">value</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;Load %v: %v: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">value</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">LoadOrStore</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;LoadOrStore %v: %v: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">key</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&quot;k5&quot;</span><span class="w"></span>
<span class="w">        </span><span class="nx">value</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;Load %v: %v: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">value</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">LoadOrStore</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;LoadOrStore %v: %v: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="p">,</span><span class="w"> </span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>

<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;Delete 方法&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">key</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&quot;k2&quot;</span><span class="w"></span>
<span class="w">        </span><span class="nx">cm</span><span class="p">.</span><span class="nx">Delete</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">key</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&quot;k21&quot;</span><span class="w"></span>
<span class="w">        </span><span class="nx">cm</span><span class="p">.</span><span class="nx">Delete</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>

<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;遍历输出map&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">cm</span><span class="p">.</span><span class="nx">Range</span><span class="w"> </span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%v: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="kc">true</span><span class="w"></span>
<span class="w">        </span><span class="p">})</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;Over&quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="bing-fa-an-quan-zi-dian-yu-yuan-sheng-zi-dian-de-bi-jiao"><a class="toclink" href="#bing-fa-an-quan-zi-dian-yu-yuan-sheng-zi-dian-de-bi-jiao">并发安全字典与原生字典的比较</a></h4>
<p>这里从3个方面对并发安全字典和原生字典进行了比较。  </p>
<h5 id="suan-fa-fu-za-du"><a class="toclink" href="#suan-fa-fu-za-du">算法复杂度</a></h5>
<p>这个字典类型提供了一些常用的键值存取操作方法，并保证了这些操作的并发安全。同时，它的存、取、删除等操作都可以基本保证在常数时间内执行完毕。就是说，它的算法复杂度与原生字典一样都是O(1)的。  </p>
<h5 id="xiao-lv-he-suo"><a class="toclink" href="#xiao-lv-he-suo">效率和锁</a></h5>
<p>与单纯使用原生字典和互斥锁的用法相比，使用并发安全字典可以显著减少锁的争夺。虽然并发安全字典本身也用到了锁，但是它在尽可能的避免使用锁。使用锁，就以为着要把一些并发的操作强制串行化，这就会降低程序的性能。因此，在讲原子操作的时候，就建议能用原子操作就不要用锁。不过当时也说了，原子操作支持的数据类型有局限性。  </p>
<h5 id="bian-yi-qi-de-zhi-chi"><a class="toclink" href="#bian-yi-qi-de-zhi-chi">编译器的支持</a></h5>
<p>无论在何种场景下使用并发安全字典，都需要记得，它与原生字典是明显不同的。并发安全字典只有Go语言标准库中的一员，而原生字典是语言层面的东西。正是因为这个原因，Go语言的编译器不会对它的键和值进行特殊的类型检查。它所有的方法涉及的键和值类型都是空接口。所以，必须在程序中自行保证它的键类型和值类型的正确性。  </p>
<h3 id="bing-fa-an-quan-zi-dian-jin-jie"><a class="toclink" href="#bing-fa-an-quan-zi-dian-jin-jie">并发安全字典进阶</a></h3>
<p>使用并发安全字典时，<strong>保证key和value的类型正确</strong>，很重要。<br>
首先，字典的key的类型是有要求的。<br>
另外，每一个具体的字典类型，它的key和value的类型是要明确的。可以使用类型断言表达式或者反射操作来保证类型正确。为了进一步明确并发安全字典中key值的实际类型，这里大致有两个方案。  </p>
<h4 id="zi-dian-key-lei-xing-de-yao-qiu"><a class="toclink" href="#zi-dian-key-lei-xing-de-yao-qiu">字典key类型的要求</a></h4>
<p>原生字典的key不能是如下的类型：</p>
<ul>
<li>不能是，函数类型</li>
<li>不能是，字典类型</li>
<li>不能是，切片类型</li>
</ul>
<p>并发安全字典的key的类型也是一样的限制（内部也是使用原生字典类型作为存储介质）。<br>
在并发安全字典内部，使用的存储介质仍然是原生字典，上面这些类型都不能用。但是由于并发安全字典的key类型的实际类型是空接口，就是可以是任何类型，所以编译器是允许使用任何类型的。如果使用了上述key不支持的类型，编译阶段是发现不了的。只要在程序运行期间才能确定键的实际类型，此时如果是不正确的key值实际类型肯定会引发panic。<br>
因此，一定不要违反字典对key类型的限制。应该在每次操作并发安全字典的时候，都去显式的检查key值的实际类型。无论是存、取、删除都是如此。<br>
更好的做法是，把针对同一个并发安全字典的几种操作都集中起来，然后统一的编写检查代码。如果是把并发安全字典封装在一个结构体里，是一个很好的选择。<br>
总之，必须保证key的类型是可比较的（或者说是可判等的）。如果实在确定不了，那么可以先通过调用reflect.TypeOf函数得到一个键对应的反射类型值（即：reflect.Type类型），然后再调用这个值的Comparable方法，得到确切的判断结果。Comparable方法返回一个布尔值，判断类型是否是可比较的，即：判断是否可以作为key的类型。  </p>
<h4 id="fang-an-yi-lei-xing-duan-yan"><a class="toclink" href="#fang-an-yi-lei-xing-duan-yan">方案一：类型断言</a></h4>
<p>利用类型断言，让并发安全字典只能存储某个特定类型的值。<br>
指定key和value的值只能是某个类型，一旦完全确定了值的类型，就可以在存、取、删除的时候，使用类型断言表达式去对key的类型做检查了。一般这种检查并不繁琐，而且要是把并发安全字典封装在一个结构体里，就更方便了。这时Go语言编译器就可以帮助完成检查的工作。就像下面这样：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// 这是一个key为字符串类型，value为数字的字典</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">StrIntMap</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">m</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">Map</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">ms</span><span class="w"> </span><span class="o">*</span><span class="nx">StrIntMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Delete</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">ms</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Delete</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">ms</span><span class="w"> </span><span class="o">*</span><span class="nx">StrIntMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">value</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ms</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">value</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">v</span><span class="p">.(</span><span class="kt">int</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">ms</span><span class="w"> </span><span class="o">*</span><span class="nx">StrIntMap</span><span class="p">)</span><span class="w"> </span><span class="nx">LoadOrStore</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">(</span><span class="nx">actual</span><span class="w"> </span><span class="kd">interface</span><span class="p">{},</span><span class="w"> </span><span class="nx">loaded</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">loaded</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ms</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">LoadOrStore</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">actual</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">a</span><span class="p">.(</span><span class="kt">int</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">ms</span><span class="w"> </span><span class="o">*</span><span class="nx">StrIntMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Range</span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">f1</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">f</span><span class="p">(</span><span class="nx">key</span><span class="p">.(</span><span class="kt">string</span><span class="p">),</span><span class="w"> </span><span class="nx">value</span><span class="p">.(</span><span class="kt">int</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">ms</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Range</span><span class="p">(</span><span class="nx">f1</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">ms</span><span class="w"> </span><span class="o">*</span><span class="nx">StrIntMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Store</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">ms</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>把并发安全字典封装到结构体中之后，要把原本字典中公开的方法在结构体上再实现一遍。只要简单的调用并发安全字典里的方法就可以了。由于自己写的结构体的方法的参数列表里已经明确的定义了变量的类型，所以就不用再做类型检查了，并且使用编译器编写代码的时候类型错误也会有提示。基于上面的情况，使用这些方法取出key和value的时候，完全不用担心类型不正确，因为正确性在最初存入的时候就已经保证了。这里在取出值的时候，由于sync.Map返回的是空接口，所以需要用类型断言做一下类型转换。这里用类型断言的时候只使用一个返回值，直接转，不用担心会有错误。  </p>
<h4 id="fang-an-er-fan-she"><a class="toclink" href="#fang-an-er-fan-she">方案二：反射</a></h4>
<p>上一个方案虽然很好，但是有一点不方便。就是封装了一大堆内容，实现了一个key-value的类型。如果还是需要另外一个类型的字典，还得再封装同样的一大堆的内容。这样的话，在需求多样化之后，工作量反而更大，而且会有很多雷同的代码。<br>
这里需要一个这样效果的方案：既要保持sync.Map类型原有的灵活性，又可以约束key和value的类型。这就需要通过反射来做类型的判断。<br>
更灵活，也更复杂，更难懂，代码更多。字典类型再多也只需要定义一次。  </p>
<h5 id="jie-gou-ti-de-lei-xing"><a class="toclink" href="#jie-gou-ti-de-lei-xing">结构体的类型</a></h5>
<p>这次在设计结构体类型的时候，只包含sync.Map类型的字段就不够了，需要向下面这样：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">ConcurrentMap</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">m</span><span class="w">         </span><span class="nx">sync</span><span class="p">.</span><span class="nx">Map</span><span class="w"></span>
<span class="w">    </span><span class="nx">keyType</span><span class="w">   </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Type</span><span class="w"></span>
<span class="w">    </span><span class="nx">valueType</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Type</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这次多定义了2个字段keyType和valueType，分别用于保存key类型和value类型。类型都是reflect.Type，就是反射类型。这个类型可以代表Go语言的任何数据类型，并且类型的值也非常容易获得：就是通过reflect.TypeOf函数并把某个样本值传入即可，比如：reflect.TypeOf(int(3))，就是int类型的反射类型值。  </p>
<h5 id="gou-zao-han-shu"><a class="toclink" href="#gou-zao-han-shu">构造函数</a></h5>
<p>首先提供一个方法，让外部的代码可以创建一个结构体：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">NewConcurrentMap</span><span class="p">(</span><span class="nx">keyType</span><span class="p">,</span><span class="w"> </span><span class="nx">valueType</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Type</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">keyType</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;key 类型为 nil&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">keyType</span><span class="p">.</span><span class="nx">Comparable</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 判断类型是否可以比较，就是是否是内做key的类型</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;不可比较的类型: %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">keyType</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">valueType</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;value 类型为 nil&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">cm</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">ConcurrentMap</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">keyType</span><span class="p">:</span><span class="w">   </span><span class="nx">keyType</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">valueType</span><span class="p">:</span><span class="w"> </span><span class="nx">valueType</span><span class="p">,</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">cm</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>创建成功，则返回结构体的指针和nil的错误类型。创建失败，则返回nil和具体的错误类型。<br>
先判断传入的类型是否为nil。另外对于key，基于对key类型的限制，必须是可比较的，这里用reflect包里的Comparable方法就可以进行判断了。  </p>
<h5 id="load-fang-fa"><a class="toclink" href="#load-fang-fa">Load方法</a></h5>
<p>结构体有了，接下来就实现所有的方法。首先是Load方法：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">cm</span><span class="w"> </span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">(</span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{},</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里的类型检查的代码非常简单。如果参数key的反射类型与keyTpye字段的反射类型不相等，那么就直接返回空值。类型都不对，那么字典里是一定没有这个key的，所以就直接返回查询不到的结果。这时，返回的是nil和false，完全符合Load方法原本的含义。  </p>
<h5 id="store-fang-fa"><a class="toclink" href="#store-fang-fa">Store方法</a></h5>
<p>Stroe方法接收2个空接口类型，没有返回值：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">cm</span><span class="w"> </span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Store</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;key类型不符合: 实际类型: %v, 需要类型: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">),</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">valueType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;value类型不符合: 实际类型: %v, 需要类型: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">value</span><span class="p">),</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">valueType</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里的做法是一旦类型检查不符，就直接引发panic。这么做主要是由于Store方法没有结果声明，就是无返回值，所以在参数值有问题的时候，无法通过比较平和的方式告知调用方。这么做也是符合Store方法的原本含义的。<br>
也可以把结构体的Store方法改的和sysc.Map里的Store方法稍微不一样一点，就是加上结果声明，返回一个error类型。这样当参数值类型不正确的时候，就返回响应的错误，而不引发panic。作为示例，就先用panic的方式吧。在实际的应用场景里可以再做优化和改进。  </p>
<h5 id="wan-zheng-de-ding-yi"><a class="toclink" href="#wan-zheng-de-ding-yi">完整的定义</a></h5>
<p>其他的方法就都差不多了，下面展示了所有定义的方法：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">ConcurrentMap</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">m</span><span class="w">         </span><span class="nx">sync</span><span class="p">.</span><span class="nx">Map</span><span class="w"></span>
<span class="w">    </span><span class="nx">keyType</span><span class="w">   </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Type</span><span class="w"></span>
<span class="w">    </span><span class="nx">valueType</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Type</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">NewConcurrentMap</span><span class="p">(</span><span class="nx">keyType</span><span class="p">,</span><span class="w"> </span><span class="nx">valueType</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">Type</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">keyType</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;key 类型为 nil&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">keyType</span><span class="p">.</span><span class="nx">Comparable</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 判断类型是否可以比较，就是是否是内做key的类型</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;不可比较的类型: %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">keyType</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">valueType</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;value 类型为 nil&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">cm</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">ConcurrentMap</span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">keyType</span><span class="p">:</span><span class="w">   </span><span class="nx">keyType</span><span class="p">,</span><span class="w"></span>
<span class="w">        </span><span class="nx">valueType</span><span class="p">:</span><span class="w"> </span><span class="nx">valueType</span><span class="p">,</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">cm</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">cm</span><span class="w"> </span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">(</span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{},</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Load</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">cm</span><span class="w"> </span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Store</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;key类型不符合: 实际类型: %v, 需要类型: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">),</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">valueType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;value类型不符合: 实际类型: %v, 需要类型: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">value</span><span class="p">),</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">valueType</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">cm</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Store</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">cm</span><span class="w"> </span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">)</span><span class="w"> </span><span class="nx">LoadOrStore</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">(</span><span class="nx">actual</span><span class="w"> </span><span class="kd">interface</span><span class="p">{},</span><span class="w"> </span><span class="nx">loaded</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;key类型不符合: 实际类型: %v, 需要类型: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">),</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">valueType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;value类型不符合: 实际类型: %v, 需要类型: %v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">value</span><span class="p">),</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">valueType</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">actual</span><span class="p">,</span><span class="w"> </span><span class="nx">loaded</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">LoadOrStore</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">cm</span><span class="w"> </span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Delete</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">reflect</span><span class="p">.</span><span class="nx">TypeOf</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">cm</span><span class="p">.</span><span class="nx">keyType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">cm</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Delete</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">cm</span><span class="w"> </span><span class="o">*</span><span class="nx">ConcurrentMap</span><span class="p">)</span><span class="w"> </span><span class="nx">Range</span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">cm</span><span class="p">.</span><span class="nx">m</span><span class="p">.</span><span class="nx">Range</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="zong-jie"><a class="toclink" href="#zong-jie">总结</a></h4>
<p>第一种方案（推荐用），适用于可以完全确定key和value的具体类型的情况。这是，可以利用Go语言的编译器去做类型检查，并用类型断言表达式作为辅助。缺陷是一次定义只能满足一种字典类型，一旦字典类型多样化，就需要编写大量的重复代码。不过还有一个好处，就是在编译阶段就可以发现类型不正确的问题。<br>
第二种方案（装逼用，至少我还没完全搞明白），在程序运行之前不用明确key和value的类型，只要在初始化并发安全字典的时候，动态的给定key和value的类型即可。主要是用到了reflect包中的方法和数据类型。灵活性高了，一次就能满足所有的字典类型。但是那些反射操作或多或少都会降低程序的性能。而且如果有类型不符合的情况，无法在编译阶段发现。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>